package com.owc.common.handler;

import com.owc.common.enums.ExceptionEnum;
import com.owc.common.exception.BadRequestException;
import com.owc.common.exception.LoginException;
import com.owc.common.exception.NoPermissionException;
import com.owc.common.exception.OWCException;
import com.owc.domain.BaseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * All controller router's exception handler
 * @author chen
 */
@RestControllerAdvice({"com.owc.controller"})
@Slf4j
public class ControllerExceptionHandler {

    /**
     * Global exception interceptor. Write to response. Just for OWCException .
     * @param e Exception example
     * @return error data
     */
    @ExceptionHandler({OWCException.class})
    public ResponseEntity<BaseResult<Object>> handleHaloException(OWCException e) {
        BaseResult<Object> baseResponse = handleBaseException(e);
        baseResponse.setCode(e.getCode());
        baseResponse.setData("");
        return new ResponseEntity<>(baseResponse, HttpStatus.OK);
    }

    /**
     * Interceptor login valid fail exception and noPermission request exception handler
     * @param le  Exception example
     * @return    error data
     */
    @ExceptionHandler({NoPermissionException.class})
    public ResponseEntity<BaseResult<Object>> handlePermissionException(NoPermissionException le){
        BaseResult<Object> baseResponse = handleBaseException(le);
        baseResponse.setCode(ExceptionEnum.TOKEN_EXPIRE_ERR.getCode());
        baseResponse.setData("");
        return new ResponseEntity<>(baseResponse, HttpStatus.OK);
    }

    @ExceptionHandler({LoginException.class})
    public ResponseEntity<BaseResult<Object>> handleLoginException(LoginException le){
        BaseResult<Object> baseResponse = handleBaseException(le);
        baseResponse.setCode(le.getCode());
        baseResponse.setData("");
        return new ResponseEntity<>(baseResponse, HttpStatus.OK);
    }

    @ExceptionHandler({BadRequestException.class})
    public ResponseEntity<BaseResult<Object>> handlerBadException(BadRequestException ex){
        BaseResult<Object> baseResponse = handleBaseException(ex);
        baseResponse.setCode(ex.getCode());
        baseResponse.setData("");
        return new ResponseEntity<>(baseResponse, HttpStatus.OK);
    }

    /**
     * Interceptor exception.
     */
    @ExceptionHandler({Exception.class})
    public ResponseEntity<BaseResult<Object>> handlerException(Exception e){
        BaseResult<Object> baseResponse = handleBaseException(e);
        baseResponse.setCode(ExceptionEnum.UNKNOWN_ERR.getCode());
        baseResponse.setData("");
        return new ResponseEntity<>(baseResponse, HttpStatus.OK);
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity<BaseResult<Object>> methodArgumentNotValidHandler(MethodArgumentNotValidException e){
        BaseResult<Object> baseResponse = new BaseResult<>();
        baseResponse.setMessage(e.getBindingResult().getFieldErrors().get(0).getDefaultMessage());
        baseResponse.setCode(ExceptionEnum.PARAMS_MISS_ERR.getCode());
        baseResponse.setData("");
        return new ResponseEntity<>(baseResponse, HttpStatus.OK);
    }

    private <T> BaseResult<T> handleBaseException(Throwable t) {
        Assert.notNull(t, "Throwable must not be null");

        log.error("Captured an exception throw message", t);

        BaseResult<T> baseResponse = new BaseResult<>();
        baseResponse.setMessage(t.getMessage());

        //If is debug level, record throw stack message.
        if (log.isDebugEnabled()) {
            baseResponse.setDevMessage(ExceptionUtils.getStackTrace(t));
        }
        return baseResponse;
    }
}
